﻿/*
Copyright (c) 2009 Vratislav Cermak (vratacermak@gmail.com)
 
This file is part of TeachMe Project. See http://code.google.com/p/teach-me for more information.
 
TeachMe is an open source project. Using of TeachMe is free of charge.
It is distributed under GNU General Public License version 3 (GPLv3).
GPLv3 can be found at http://www.gnu.org/licenses/gpl.html.
 
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.  See the license for more details.
*/

using System;
using System.Collections.Generic;
using System.Drawing.Text;
using System.IO;
using System.Reflection;
using System.Xml.Serialization;
using TeachMe.Application.Enums;

namespace TeachMe.Application.Configuration
{
    public class UserSettings : Checkable
    {
        public static int MaxEntriesCount = 5;

        private string _path;

        private List<Hotkey> _hotkeys;
        private List<DictionaryUserData> _dictionariesUserData;
        private Property<string> _id;
        private Property<string> _language;
        private Property<int> _entryOpacity;
        private Property<int> _newEntryInterval;
        private Property<string> _currentThemePath;
        private Property<string> _themesDirectory;
        private Property<int> _entriesHorizontalDistance;
        private Property<int> _entriesVerticalDistance;
        private Property<int> _maxEntriesNumber;
        private Property<int> _hintInterval;
        private Property<int> _veryLowPriorityRate;
        private Property<int> _lowPriorityRate;
        private Property<int> _normalPriorityRate;
        private Property<int> _highPriorityRate;
        private Property<NewEntryMode> _newEntryMode;
        private Property<EntryRevealReason> _entryRevealReason;
        private Property<string> _suspendIntervals;
        private Property<string> _maxEntriesNumbers;
        private Property<int> _turboModeInterval;
        private Property<int> _hotkeySuspendInterval;
        private BooleanProperty _topMostEntries;
        private Property<string> _importCodePage;
        private Property<int> _minImportedWordLen;
        private BooleanProperty _translateAutomatically;
        private BooleanProperty _useRegExForExcludedWords;
        private Property<Interval> _newVersionCheckInterval;
        private BooleanProperty _highlightFromInExample;
        private BooleanProperty _editEntriesDirectly;
        private Property<string> _symbols;
        private BooleanProperty _showSymbols;
        private Property<FontSize> _entryFontSize;
        private CsvImportDefaults _csvImportDefaults;
        private Property<TextRenderingHint> _textRenderingHint;
        private BooleanProperty _respawnMouseHooks;
        private Property<SoundConfiguration> _soundConfiguration;
        private PexesoSettings _pexesoSettings;

        [XmlElement(ElementName = "CurrentThemePath")]
        public Property<string> TransientCurrentThemePath
        {
            get
            {
                return _currentThemePath ?? (_currentThemePath = new Property<string>(this));
            }
            set
            {
                _currentThemePath = value;
            }
        }

        [XmlIgnore]
        public string CurrentThemePath
        {
            get
            {
                return PathsProvider.MakeTransient(TransientCurrentThemePath.Value);
            }
            set
            {
                TransientCurrentThemePath.Value = PathsProvider.MakePersistent(value);
            }
        }

        [XmlElement(ElementName = "ThemesDirectory")]
        public Property<string> TransientThemesDirectory
        {
            get
            {
                if (_themesDirectory == null)
                {
                    _themesDirectory = new Property<string>(this);
                }
                return _themesDirectory;
            }
            set
            {
                _themesDirectory = value;
            }
        }

        [XmlIgnore]
        public string ThemesDirectory
        {
            get
            {
                return PathsProvider.MakeTransient(TransientThemesDirectory.Value);
            }
            set
            {
                TransientThemesDirectory.Value = PathsProvider.MakePersistent(value);
            }
        }

        public Property<int> EntriesHorizontalDistance
        {
            get
            {
                if (_entriesHorizontalDistance == null)
                {
                    _entriesHorizontalDistance = new Property<int>(this, 10);
                }
                return _entriesHorizontalDistance;
            }
            set
            {
                _entriesHorizontalDistance = value;
            }
        }

        public Property<int> EntriesVerticalDistance
        {
            get
            {
                if (_entriesVerticalDistance == null)
                {
                    _entriesVerticalDistance = new Property<int>(this, 10);
                }
                return _entriesVerticalDistance;
            }
            set { _entriesVerticalDistance = value; }
        }

        public Property<int> EntryOpacity
        {
            get
            {
                if (_entryOpacity == null)
                {
                    _entryOpacity = new Property<int>(this, 220);
                }
                return _entryOpacity;
            }
            set
            {
                _entryOpacity = value;
            }
        }

        public Property<int> NewEntryInterval
        {
            get
            {
                if (_newEntryInterval == null)
                {
                    _newEntryInterval = new Property<int>(this, 1);
                }
                return _newEntryInterval;
            }
            set
            {
                _newEntryInterval = value;
            }
        }

        public List<DictionaryUserData> DictionariesUserData
        {
            get
            {
                if (_dictionariesUserData == null)
                {
                    _dictionariesUserData = new List<DictionaryUserData>();
                }
                return _dictionariesUserData;
            }
            set
            {
                _dictionariesUserData = value;
            }
        }

        public Property<int> MaxEntriesNumber
        {
            get
            {
                if (_maxEntriesNumber == null)
                {
                    _maxEntriesNumber = new Property<int>(this, 3);
                }

                // fix invalid entries count
                if (_maxEntriesNumber.Value > MaxEntriesCount || _maxEntriesNumber.Value <= 0)
                {
                    _maxEntriesNumber.Value = MaxEntriesCount;
                }

                return _maxEntriesNumber;
            }
            set
            {
                _maxEntriesNumber = value;
            }
        }

        public Property<int> HintInterval
        {
            get { return _hintInterval ?? (_hintInterval = new Property<int>(this, 5)); }
            set
            {
                _hintInterval = value;
            }
        }

        public Property<NewEntryMode> NewEntryMode
        {
            get
            {
                if (_newEntryMode == null)
                {
                    _newEntryMode = new Property<NewEntryMode>(this, Application.NewEntryMode.Persistent);
                }
                return _newEntryMode;
            }
            set
            {
                _newEntryMode = value;
            }
        }

        public Property<int> LowPriorityRate
        {
            get
            {
                if (_lowPriorityRate == null)
                {
                    _lowPriorityRate = new Property<int>(this, 15);
                }
                return _lowPriorityRate;
            }
            set
            {
                _lowPriorityRate = value;
            }
        }

        public Property<int> NormalPriorityRate
        {
            get
            {
                if (_normalPriorityRate == null)
                {
                    _normalPriorityRate = new Property<int>(this, 30);
                }
                return _normalPriorityRate;
            }
            set
            {
                _normalPriorityRate = value;
            }
        }

        public Property<int> HighPriorityRate
        {
            get
            {
                if (_highPriorityRate == null)
                {
                    _highPriorityRate = new Property<int>(this, 50);
                }
                return _highPriorityRate;
            }
            set
            {
                _highPriorityRate = value;
            }
        }

        public Property<int> VeryLowPriorityRate
        {
            get
            {
                if (_veryLowPriorityRate == null)
                {
                    _veryLowPriorityRate = new Property<int>(this, 5);
                }
                return _veryLowPriorityRate;
            }
            set
            {
                _veryLowPriorityRate = value;
            }
        }

        public Property<EntryRevealReason> EntryRevealReason
        {
            get
            {
                if (_entryRevealReason == null)
                {
                    _entryRevealReason = new Property<EntryRevealReason>(this, Application.EntryRevealReason.Interval);
                }
                return _entryRevealReason;
            }
            set { _entryRevealReason = value; }
        }

        public Property<string> Id
        {
            get
            {
                if (_id == null)
                {
                    _id = new Property<string>(this, Guid.NewGuid().ToString());
                }
                return _id;
            }
            set
            {
                _id = value;
            }
        }

        public Property<string> SuspendIntervals
        {
            get
            {
                if (_suspendIntervals == null)
                {
                    _suspendIntervals = new Property<string>(this, "10; 30; 60; 120; 240");
                }
                return _suspendIntervals;
            }
            set
            {
                _suspendIntervals = value;
            }
        }

        public Property<string> MaxEntriesNumbers
        {
            get
            {
                return _maxEntriesNumbers ?? (_maxEntriesNumbers = new Property<string>(this, "1; 2; 3; 4; 5"));
            }
            set
            {
                _maxEntriesNumbers = value;
            }
        }

        public Property<int> TurboModeInterval
        {
            get
            {
                if (_turboModeInterval == null)
                {
                    _turboModeInterval = new Property<int>(this, 2);
                }
                return _turboModeInterval;
            }
            set { _turboModeInterval = value; }
        }

        public Property<int> HotkeySuspendInterval
        {
            get
            {
                if (_hotkeySuspendInterval == null)
                {
                    _hotkeySuspendInterval = new Property<int>(this, 30);
                }
                return _hotkeySuspendInterval;
            }
            set { _hotkeySuspendInterval = value; }
        }

        public BooleanProperty TopMostEntries
        {
            get
            {
                if (_topMostEntries == null)
                {
                    _topMostEntries = new BooleanProperty(this, true);
                }
                return _topMostEntries;
            }
            set { _topMostEntries = value; }
        }

        public List<Hotkey> Hotkeys
        {
            get
            {
                if (_hotkeys == null)
                {
                    _hotkeys = new List<Hotkey>();
                }
                return _hotkeys;
            }
            set { _hotkeys = value; }
        }

        public Property<string> Language
        {
            get
            {
                if (_language == null)
                {
                    _language = new Property<string>(this);
                }
                return _language;
            }
            set { _language = value; }
        }

        public Property<string> ImportEncoding
        {
            get
            {
                if (_importCodePage == null)
                {
                    _importCodePage = new Property<string>(this, "Windows-1252");
                }
                return _importCodePage;
            }
            set { _importCodePage = value; }
        }

        public Property<int> MinImportedWordLen
        {
            get
            {
                if (_minImportedWordLen == null)
                {
                    _minImportedWordLen = new Property<int>(this, 3);
                }
                return _minImportedWordLen;
            }
            set
            {
                _minImportedWordLen = value;
            }
        }

        public BooleanProperty RespawnMouseHooks
        {
            get
            {
                if (_respawnMouseHooks == null)
                {
                    _respawnMouseHooks = new BooleanProperty(this, true);
                }
                return _respawnMouseHooks;
            }
            set
            {
                _respawnMouseHooks = value;
            }
        }

        public Property<SoundConfiguration> SoundConfiguration
        {
            get
            {
                if (_soundConfiguration == null)
                {
                    _soundConfiguration = new Property<SoundConfiguration>(this, Application.SoundConfiguration.Default);
                }
                return _soundConfiguration;
            }
            set
            {
                _soundConfiguration = value;
            }
        }

        public BooleanProperty TranslateAutomatically
        {
            get
            {
                if (_translateAutomatically == null)
                {
                    _translateAutomatically = new BooleanProperty(this, true);
                }
                return _translateAutomatically;
            }
            set
            {
                _translateAutomatically = value;
            }
        }

        public BooleanProperty UseRegExForExcludedWords
        {
            get
            {
                if (_useRegExForExcludedWords == null)
                {
                    _useRegExForExcludedWords = new BooleanProperty(this, true);
                }
                return _useRegExForExcludedWords;
            }
            set
            {
                _useRegExForExcludedWords = value;
            }
        }

        public BooleanProperty HighlightFromInExample
        {
            get { return _highlightFromInExample ?? (_highlightFromInExample = new BooleanProperty(this, true)); }
            set { _highlightFromInExample = value; }
        }

        public Property<Interval> NewVersionCheckInterval
        {
            get
            {
                if (_newVersionCheckInterval == null)
                {
                    _newVersionCheckInterval = new Property<Interval>(this, Interval.Daily);
                }
                return _newVersionCheckInterval;
            }
            set
            {
                _newVersionCheckInterval = value;
            }
        }

        public Property<string> Symbols
        {
            get
            {
                return _symbols ?? (_symbols = new Property<string>(this, "ɑɐɒæɓʙßɔɕçɗɖðʤəɘɚɛɜɝɞɟʄgɠɢʛɦɧħɥʜɨɪʝɭɬɫɮʟɱɯɰŋɳɲɴøɵɸθœɶʘɹɺɾɻʀʁɽʂʃʈʧʉʋʊʌɣɤʍχʎʏʑʐʒʔʡʕʢ|"));
            }
            set
            {
                _symbols = value;
            }
        }

        public BooleanProperty ShowSymbols
        {
            get
            {
                return _showSymbols ?? (_showSymbols = new BooleanProperty(this, false));
            }
            set
            {
                _showSymbols = value;
            }
        }

        public BooleanProperty EditEntriesDirectly
        {
            get
            {
                return _editEntriesDirectly ?? (_editEntriesDirectly = new BooleanProperty(this, false));
            }
            set
            {
                _editEntriesDirectly = value;
            }
        }

        public Property<FontSize> EntryFontSize
        {
            get
            {
                return _entryFontSize ?? (_entryFontSize = new Property<FontSize>(this, FontSize.Default));
            }
            set
            {
                _entryFontSize = value;
            }
        }

        public Property<TextRenderingHint> TextRenderingHint
        {
            get
            {
                return _textRenderingHint ?? (_textRenderingHint = new Property<TextRenderingHint>(this, System.Drawing.Text.TextRenderingHint.SystemDefault));
            }
            set
            {
                _textRenderingHint = value;
            }
        }

        public CsvImportDefaults CsvImport
        {
            get
            {
                return _csvImportDefaults ?? (_csvImportDefaults = CsvImportDefaults.Create(this));
            }
            set
            {
                _csvImportDefaults = value;
            }
        }

        public PexesoSettings PexesoSettings
        {
            get { return _pexesoSettings ?? (_pexesoSettings = PexesoSettings.Create(this)); }
            set { _pexesoSettings = value; }
        }

        public static UserSettings Load(string path)
        {
            UserSettings userSettings = null;

            if (File.Exists(path))
            {
                Logging.Debug(typeof(UserSettings), "Loading user settings from '{0}'.", path);

                try
                {
                    XmlSerializer serializer = new XmlSerializer(typeof(UserSettings));
                    using (FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Read))
                    {
                        userSettings = serializer.Deserialize(stream) as UserSettings;
                    }
                }
                catch (Exception e)
                {
                    Logging.Error(typeof(UserSettings), e, "User settings could not be read.", path);
                }
            }
            if (userSettings == null)
            {
                Logging.Debug(typeof(UserSettings), "Default user settings will be created.", path);

                userSettings = new UserSettings();
            }

            userSettings._path = path;

            PropertyInfo[] properties = typeof(UserSettings).GetProperties();

            foreach (PropertyInfo propertyInfo in properties)
            {
                object value = propertyInfo.GetValue(userSettings, null);

                if (value is IProperty)
                {
                    ((IProperty)value).SetParent(userSettings);
                }
            }

            return userSettings;
        }

        protected override void Save()
        {
            try
            {
                Logging.Debug(GetType(), "Saving user settings to '{0}'.", _path);

                XmlSerializer serializer = new XmlSerializer(typeof(UserSettings));
                using (FileStream stream = new FileStream(_path, FileMode.Create, FileAccess.Write))
                {
                    serializer.Serialize(stream, this);
                }
            }
            catch (Exception e)
            {
                Logging.Error(GetType(), e, "User settings could not be saved to '{0}'.", _path);
            }
        }
    }
}
